인터랙티브 지도 제작 도구 탐구

Author

김우형

사용 가능한 패키지

1. leaflet.minicharts

종교 현황(p.169)

https://cran.r-project.org/web/packages/leaflet.minicharts/vignettes/introduction.html

# Packages Loading --------------------------------------------------------

library(sf)
library(readxl)
library(tmap)
library(tidyverse)
library(leaflet)
library(leaflet.minicharts)
# Data Preparing ----------------------------------------------------------

sido <- st_read("sido.shp", options = "ENCODING=EUC-KR")
options:        ENCODING=EUC-KR 
Reading layer `sido' from data source 
  `C:\Users\Woohyung Kim\Desktop\R_projects\0.Research\2024-2_Interactive_Map\sido.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 17 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 746255.1 ymin: 1458982 xmax: 1387941 ymax: 2068161
Projected CRS: Korea_2000_Korea_Unified_Coordinate_System
sido <- sido |> st_transform(crs = 4326)
coords <- as.data.frame(st_coordinates(st_centroid(sido)))
Warning: st_centroid assumes attributes are constant over geometries
religion <- read_xlsx("religion.xlsx")
religion <- religion |> 
  cbind(coords)
religion
   SD_CD           sido    불교  개신교  천주교  기타 종교 없음    total
1     11     서울특별시 1023721 2286305 1012892 63909   5057969  9444796
2     21     부산광역시  958683  407659  180815 27458   1785331  3359946
3     22     대구광역시  571511  288540  185546 15986   1341163  2402746
4     23     인천광역시  244467  642515  265369 18389   1612825  2783565
5     24     광주광역시  139030  292140  126284 11649    892024  1461127
6     25     대전광역시  209450  327421  110724  9784    842141  1499520
7     26     울산광역시  333441  122159   47448  7297    610180  1120525
8     29 세종특별자치시   27374   39328   15528  1143    114278   197651
9     31         경기도 1267172 2729767 1065430 78319   6728350 11869038
10    32         강원도  242579  258660   98521  9613    865951  1475324
11    33       충청북도  250007  241747  112512  7622    917093  1528981
12    34       충청남도  277823  416916  124602 12751   1180400  2012492
13    35       전라북도  152742  480150  132948 36400    980051  1782291
14    36       전라남도  189332  404287   97533 17779   1032568  1741499
15    37       경상북도  654091  345238  135299 19677   1435737  2590042
16    38       경상남도  941750  334671  132817 26534   1764435  3200207
17    39 제주특별자치도  136159   58258   46043  3960    338219   582639
          X        Y
1  126.9918 37.55203
2  129.0590 35.20026
3  128.5654 35.82969
4  126.3900 37.58123
5  126.8354 35.15571
6  127.3939 36.33984
7  129.2385 35.55348
8  127.2587 36.56069
9  127.1752 37.53240
10 128.3009 37.72516
11 127.8304 36.73786
12 126.8490 36.52987
13 127.1389 35.71640
14 126.9011 34.87609
15 128.7484 36.34742
16 128.2617 35.32395
17 126.5541 33.38653
# Mapping -----------------------------------------------------------------


tilesURL <- "http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}"

basemap <- leaflet(width = "100%", height = "800px") |> 
  addTiles(tilesURL)

colors <- c("#7161A3", "#F5A8B3", "#BE475A", "#B8B8B8", "#57C2DC")

basemap |> 
  addMinicharts(
    religion$X, religion$Y,
    type = "pie",
    chartdata = religion[, c("불교", "개신교", "천주교", "기타", "종교 없음")],
    colorPalette = colors,
    width = 70 * sqrt(religion$total) / sqrt(max(religion$total)),
    transitionTime = 0
  ) |> 
  addLabelOnlyMarkers(
    lng = religion$X,
    lat = religion$Y,
    label = lapply(
      paste0("<strong>", religion$sido, "</strong><br>", format(religion$total, big.mark = ",")),
      htmltools::HTML
    ),
    labelOptions = labelOptions(
      noHide = TRUE,
      direction = "bottom",
      textOnly = TRUE,
      style = list(
        "color" = "black",
        "font-size" = "10px",
        "font-family" = "Arial",
        "font-weight" = "bold"
      )
    )
  )

2. chorddiag()

권역별 귀농, 귀어, 귀촌 인구(p.181)

https://r-graph-gallery.com/package/chorddiag.html

# Packages Loading -----------------------------------------------------------------

library(devtools)
devtools::install_github("mattflor/chorddiag")
library(chorddiag)
library(readxl)
# Data Preparing -----------------------------------------------------------------

return <- read_xlsx("return.xlsx")
return <- as.matrix(return)

# Plot -----------------------------------------------------------------

# A vector of 6 colors for 6 groups
haircolors <- c("수도권", "강원권", "충청권", "영남권", "호남권", "제주권")
dimnames(return) <- list(
  have = haircolors,
  prefer = haircolors
)
groupColors <- c("#FF6F61", "#6B5B95", "#88B04B", "#FFA177", "#F7CAC9", "#92A8D1")

# Build the chord diagram:
p <- chorddiag(return, 
               groupColors = groupColors,
               groupnamePadding = 20
               )
p <- htmlwidgets::onRender(p, "
  function(el, x) {
    var labels = el.querySelectorAll('text');
    labels.forEach(function(label) {
      label.style.fontFamily = 'Arial';  // 원하는 글꼴로 변경
    });
  }
")
p
# Save the plot as an HTML file
htmlwidgets::saveWidget(p, "return_chorddiagram.html")

3. plotly.express

interactive sunburst chart

import plotly.express as px
import pandas as pd

df = pd.read_excel("/content/drive/MyDrive/Colab Notebooks/marriage.xlsx")
df

fig = px.sunburst(df, path = ['sex', 'type', 'age'], values = 'count',
                  color = 'type')

https://plotly.com/python/sunburst-charts/

4. ggiraph

시군구 중위 연령(p.114)

https://r-graph-gallery.com/414-map-multiple-charts-in-ggiraph.html

# Loading Packages --------------------------------------------------------

library(readxl)
library(sf)
library(tidyverse)
library(ggiraph)
Warning: package 'ggiraph' was built under R version 4.3.3
library(patchwork)
Warning: package 'patchwork' was built under R version 4.3.3
library(tmap)

# Data Preparing ----------------------------------------------------------

median <- read_xlsx("median.xlsx")
sgg <- st_read("bnd_sigungu_00_2020_2020_4Q.shp", options = "ENCODING=EUC-KR")
options:        ENCODING=EUC-KR 
Reading layer `bnd_sigungu_00_2020_2020_4Q' from data source 
  `C:\Users\Woohyung Kim\Desktop\R_projects\0.Research\2024-2_Interactive_Map\bnd_sigungu_00_2020_2020_4Q.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 250 features and 5 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 746111 ymin: 1458603 xmax: 1387949 ymax: 2068444
Projected CRS: Korea_2000_Korea_Unified_Coordinate_System
sd <- st_read("sido.shp", options = "ENCODING=EUR-KR")
options:        ENCODING=EUR-KR 
Reading layer `sido' from data source 
  `C:\Users\Woohyung Kim\Desktop\R_projects\0.Research\2024-2_Interactive_Map\sido.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 17 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 746255.1 ymin: 1458982 xmax: 1387941 ymax: 2068161
Projected CRS: Korea_2000_Korea_Unified_Coordinate_System
sgg$SIGUNGU_CD <- sgg$SIGUNGU_CD |> 
  as.double()

sgg_median <- sgg |> 
  left_join(median, join_by(SIGUNGU_CD == code))

# 37, 22, 26
median2 <- median |> 
  filter(grepl("^(22|26|37)", code))
sgg_median2 <- sgg_median |> 
  filter(grepl("^(22|26|37)", SIGUNGU_CD))
  

write_rds(sgg_median, "sgg_median.rds")

sd_union <- sd |> 
  st_union() |> 
  st_sf()

# Ploting ----------------------------------------------------------

p1 <- sgg_median2 |> 
  ggplot(aes(
    x = reorder(sgg, median),
    y = median,
    tooltip = sgg,
    data_id = sgg,
    fill = median)) +
  geom_col_interactive(data = filter(median2, !is.na(median))) +
  scale_fill_gradientn_interactive(colors = c("#edf8e9", "#bae4b3", "#74c476", "#31a354", "#006837")) +
  coord_flip() +
  theme_minimal() +
  theme(
    axis.text = element_text(size = 5),
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "none"
  )

p2 <- ggplot() +
  geom_sf(data = sgg_median2) +
  geom_sf_interactive(
    data = filter(sgg_median2, !is.na(median)),
    aes(fill = median, tooltip = sgg, data_id = sgg)
  ) +
  scale_fill_gradientn_interactive(colors = c("#edf8e9", "#bae4b3", "#74c476", "#31a354", "#006837"),
                                   name = "중위연령(세)",
                                   breaks = c(41, 45, 49, 53, 57),  # 레전드에 표시할 값
                                   labels = c("41", "45", "49", "53", "57")) +
  coord_sf(crs = st_crs(5179)) +
  theme_void() +
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    legend.position = "right",
    legend.title = element_text(size = 5),
    legend.text = element_text(size = 3),
    legend.key.size = unit(0.3, 'cm')
  )

combined_plot <- (p1 + p2) + plot_layout(heights = c(3, 1), widths = c(1, 2))

interactive_plot <- girafe(ggobj = combined_plot)
interactive_plot <- girafe_options(
  interactive_plot,
  opts_hover(css = "fill:red;stroke:black;")
)
interactive_plot

추가적으로 알아볼 것

deck.gl

https://deck.gl/examples

pydeck

https://deckgl.readthedocs.io/en/latest/

kepler.gl

https://velog.io/@tyhlife/Jupyter-Notebook%EC%97%90%EC%84%9C-Kepler%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%A7%80%EB%A6%AC%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0